import * as THREE from 'three';
import { loadByUrl } from '../tools/loader';
import ResourceTracker from '../tools/ResourceTracker';
import { Y_LAYERS } from '../tools/constants';
import BaseModel from '../BaseModel';

/**
 * @description 转换 单个MarkerJS 的原始数据为渲染数据
 * @param rawData - 原始数据
 * @param _opt - 选项对象
 * @returns 渲染数据
 */
function _convertMarkJSRawData(
  plotRawData: Editor.CropPlotRawData,
  _opt: {
    size: THREE.Vector3;
  }
): Array<[number, number]> {
  const { size } = _opt;
  let { x: xLength, z: yLength } = size;
  const { pointArr } = plotRawData;
  const plotRenderData: Array<[number, number]> = [];

  pointArr.forEach((point: { x: number; y: number }) => {
    const { x, y } = point;
    const xPoint = +x * xLength;
    // MARK: Y轴翻转
    const yPoint = (1 - y) * yLength;
    plotRenderData.push([xPoint, yPoint]);
  });
  return plotRenderData;
}

/**
 * @description 转换crop百分比数据到Vec3渲染数据
 */
function _convertCropRawData(
  rawData: Array<{ x: number; y: number }>,
  _opt: {
    size: THREE.Vector3;
  }
): Array<THREE.Vector3> {
  const cropRenderData = [];
  if (Array.isArray(rawData) && rawData.length > 0) {
    const { size } = _opt;
    let { x: xLength, z: yLength } = size;
    rawData.forEach((point) => {
      const { x, y } = point;
      const xPoint = x * xLength;
      // MARK: 符号取反
      const zPoint = -(1 - y) * yLength;
      cropRenderData.push(new THREE.Vector3(xPoint, Y_LAYERS.CROP_PLOT, zPoint));
    });
  }
  return cropRenderData;
}

/**
 * @description 根据plot矩阵的一号点[左上]和三号点[右下]计算crop分布的百分比坐标
 */
function getCropCoordinate(
  firstPoint: { x: number; y: number },
  thirdPoint: { x: number; y: number },
  rowSpacing: number,
  colSpacing: number
): Array<{ x: number; y: number }> {
  const { x: x1, y: y1 } = firstPoint;
  const { x: x2, y: y2 } = thirdPoint;

  const cropCoordinate = [];

  function getOffset(leftBound: number, rightBound: number, spacing: number) {
    let end = leftBound - spacing / 2;
    for (; end + spacing <= rightBound; end += spacing) {}

    const margin = (spacing / 2 + rightBound - end) / 2;
    return (margin < spacing / 2 ? -1 : 1) * Math.abs(margin - spacing / 2);
  }

  const colMargin = getOffset(x1, x2, colSpacing);
  const rowMargin = getOffset(y1, y2, rowSpacing);

  for (let x = x1 + colSpacing / 2; x <= x2; x += colSpacing) {
    for (let y = y1 + rowSpacing / 2; y <= y2; y += rowSpacing) {
      cropCoordinate.push({
        x: x + colMargin,
        y: y + rowMargin,
      });
    }
  }

  if (cropCoordinate.length === 0) {
    return [{ x: (x1 + x2) / 2, y: (y1 + y2) / 2 }];
  }

  return cropCoordinate;
}

export interface CropPlotOption {
  /** 该数据中包含id */
  rawData: Editor.CropPlotRawData;
  building: Editor.Building;
  /** 父级group */
  cropStaffGroup: Editor.PlantAreaManager['cropStaffGroup'];
  /** 作物模型信息 */
  cropModelInfo?: CropModelInfo;
}

interface CropModelInfo {
  url: string;
  /** 默认为1 不缩放 */
  scale?: number;
  /** TODO: xspaing x方向种植间距*/
  // xSpacing?: number;
  /** TODO: zspaing z方向种植间距*/
  // zSpacing?: number;
}

export interface CropPlotSaveData extends Editor.CropPlotRawData {
  cropModelInfo: CropModelInfo;
}

/**
 * TODO:
 *  作物模型支持缓存和预渲染(参考同级cropModelInfo)
 * 作物地块类，用于在Three.js场景中渲染可交互的种植地块
 */
export default class CropPlot {
  type = 'CropPlot';
  /** 地块网格对象 */
  model: THREE.Mesh | null = null;

  /** 作物模型数组 */
  cropObjects: Array<THREE.Object3D> = [];
  cropModelInfo: CropModelInfo = {
    url: null,
    scale: 1,
  };
  /** @deprecated 缓存的作物模型URL */
  _cacheCropModelUrl: string = '';

  group = new THREE.Group();

  cropStaffGroup: Editor.PlantAreaManager['cropStaffGroup'];

  /** 原始数据 */
  rawData: Editor.CropPlotRawData = null;
  /** MARK: 初始渲染points，用于changeScale时保持基本形状(长度为4) */
  points: Array<[number, number]> = [];

  /** 泥土材质 */
  mudMaterial: THREE.Material | null = null;

  option: Editor.CropPlotOption = null;

  /** 资源追踪器 */
  resourceTracker = new ResourceTracker();
  cropRecourceTracker = new ResourceTracker();

  /**
   * 构造函数
   * @param cropPlotOption 作物地块选项
   */
  constructor(cropPlotOption: Editor.CropPlotOption) {
    const { rawData, building, cropModelInfo } = cropPlotOption;
    this.rawData = rawData;
    this.option = cropPlotOption;
    this._create();
    // 预渲染一次作物模型
    this.changeCrop(cropModelInfo);
    building.on(BaseModel.EVENT_BASEMODEL_SCALE_CHANGED, this._updateRawDataPointArr);
  }

  /**
   * 渲染CropPlot
   * @param points 地块顶点数据
   * @param materialOptions 材质选项
   * @returns 渲染的网格对象
   */
  private _create(
    /** MARK 只在updateShape时传入 */
    plotRawData?: Editor.CropPlotRawData,
    materialOptions: {
      material?: THREE.Material;
      color?: number;
      doubleSided?: boolean;
    } = {}
  ): void {
    // MARK: 合并原始数据，传入的优先
    plotRawData = plotRawData || this.rawData;
    // 转换数据
    const { building, cropStaffGroup } = this.option;
    const { size } = building.getInfo();
    // 保存初始points
    const points = _convertMarkJSRawData(plotRawData, { size });
    this.points = points;
    if (!Array.isArray(points) || points.length < 3) {
      console.error('多边形至少需要3个点');
      return null;
    }

    const { material, color = 0x569230, doubleSided = true } = materialOptions;

    // 创建材质（如果未提供自定义材质）
    const polygonMaterial =
      material ||
      new THREE.MeshBasicMaterial({
        color,
        transparent: false,
        side: doubleSided ? THREE.DoubleSide : THREE.FrontSide,
      });

    // 创建Three.js的Shape对象
    const shape = new THREE.Shape();
    // 添加轮廓点
    shape.moveTo(points[0][0], points[0][1]);
    for (let i = 1; i < points.length; i++) {
      shape.lineTo(points[i][0], points[i][1]);
    }
    shape.closePath(); // 确保多边形闭合
    // 创建几何体
    const geometry = new THREE.ShapeGeometry(shape);
    // 创建网格
    const mesh = new THREE.Mesh(geometry, polygonMaterial);
    // TODO: 需要根据模型朝向进行旋转
    mesh.rotateX(-Math.PI / 2);

    mesh.position.set(0, Y_LAYERS.CROP_PLOT, 0);
    // 资源追踪
    this.resourceTracker.track(mesh);
    // 数据更新
    this.model = mesh;
    this.rawData = plotRawData;

    // group设置
    this.group.name = `cropPlot-${this.getId()}`;
    this.group.add(mesh);
    cropStaffGroup.add(this.group);
  }

  /**
   * @description 将plot及其作物从group中移除，但保留其他数据
   */
  remove() {
    // 移除作物
    this.removeAllCrops();
    // 移除plot
    this.group.remove(this.model);
    this.model = null;
    this.resourceTracker.dispose();
  }

  /** @description 当building触发changeScale时，应该触发该方法 */
  private _updateRawDataPointArr = () => {
    const { building } = this.option;
    const { size: currentSize } = building.getInfo();
    const newPointArr = [];
    this.points.forEach((point) => {
      const x = point[0];
      const y = point[1];
      newPointArr.push({
        x: x / currentSize.x,
        y: 1 - y / currentSize.z,
      });
    });
    this.rawData.pointArr = newPointArr;
  };
  /**
   * 更新地块形状
   * @param points 新的地块顶点数据
   */
  updateShape(plotRawData: Editor.CropPlotRawData) {
    // 深拷贝一份cropModeInfo
    let deepCachecCropModel = JSON.parse(JSON.stringify(this.cropModelInfo));
    // 移除旧的mesh
    this.remove();
    // 创建新的mesh
    this._create(plotRawData);
    // 恢复mud
    this.mudMaterial && (this.model.material = this.mudMaterial);
    // 恢复crop
    this.changeCrop(deepCachecCropModel);
    // 释放深拷贝
    deepCachecCropModel = null;
  }

  /**
   * 更改泥土材质
   * @param option 材质选项
   */
  changeMud(option: { url: string }) {
    const { url } = option;
    const loader = new THREE.TextureLoader();
    loader.load(url, (texture) => {
      texture.wrapS = THREE.RepeatWrapping;
      texture.wrapT = THREE.RepeatWrapping;
      // 创建新材质并应用纹理
      const material = new THREE.MeshStandardMaterial({
        map: texture,
        // side: this.model.material.side,
        transparent: false,
      });
      this.model.material = material;
      this.mudMaterial = material;
    });
  }

  /**
   * 渲染作物
   * @param cropModel 作物模型
   */
  private _renderCrop(cropModel: THREE.Object3D) {
    const { pointArr } = this.getRawData();

    // 得到作物坐标
    const cropCoordinate = getCropCoordinate(pointArr[0], pointArr[2], 0.2, 0.2);

    const { building } = this.option;
    const { size } = building.getInfo();
    // 得到三维坐标
    const vec3Points = _convertCropRawData(cropCoordinate, {
      size,
    });

    // 最终渲染
    vec3Points.forEach((vec3Point) => {
      const newModel = cropModel.clone();
      newModel.position.copy(vec3Point);
      this.cropObjects.push(newModel);
      this.cropRecourceTracker.track(newModel);
      this.group.add(newModel);
    });
  }

  /**
   * 更改作物
   * TODO: 功能增强
   *  - 修改作物类型 - 通过url映射
   *  - 修改作物的种植密度 - 通过spacing
   * @param option 作物选项
   */
  changeCrop(option: { url: string; scale?: number }) {
    if (!option) return;
    const { url, scale = 1 } = option;
    // url为空或与当前相同时，不进行任何操作
    if (typeof url !== 'string' || url === this.cropModelInfo?.url) return;
    loadByUrl(url, scale).then((model) => {
      this.removeAllCrops();
      this._renderCrop(model);
      this.cropModelInfo = Object.assign(this.cropModelInfo, option);
    });
  }

  getRawData(): Editor.CropPlotRawData {
    return this.rawData;
  }

  getSaveData(): CropPlotSaveData {
    return {
      cropModelInfo: this.cropModelInfo,
      ...this.getRawData(),
    };
  }

  /** @description 获取cropPlot的唯一id */
  getId(): string | number {
    const { data } = this.rawData;
    return data.id;
  }

  /**
   * @description 移除所有作物并重置cropModelInfo
   */
  removeAllCrops() {
    // 移除所有crop模型
    this.cropObjects.forEach((crop) => {
      this.group.remove(crop);
    });
    this.cropObjects = [];
    this.cropRecourceTracker.dispose();
    // 重置cropModelInfo
    this.cropModelInfo = {
      url: null,
      scale: 1,
    };
  }

  destroy() {
    const { building, cropStaffGroup } = this.option;
    // 移除监听
    building.off(BaseModel.EVENT_BASEMODEL_SCALE_CHANGED, this._updateRawDataPointArr);
    this.remove();
    // 移除group
    cropStaffGroup.remove(this.group);
    this.group = null;
    this.points = null;
    this.resourceTracker = null;
    this.cropRecourceTracker = null;
  }
}
